import { DEFAULT_OPTS, TOKEN, LOAD_EVENT_NAME } from './constant/index.js'
import Popup from './popup/index.js'
import EventEmit from './event/eventEmit'
import entityMethods from './entity/index.js'
import layerMethods from './layer/index.js'
import primitiveMethods from './primitive/index.js'
import { loadCss, loadJs, isFunction, coordinateUtils } from './utils/index.js'

export default class CsmMap extends EventEmit {

  constructor(containerId, config = {}) {
    super()
    const opts = { ...DEFAULT_OPTS, ...config }
    this.viewer = null

    initCache.call(this)
    // 初始化资源
    if (!window.Cesium) {
      loadRecource(opts).then(() => {
        initMap.call(this, containerId, opts)
      })
    } else {
      initMap.call(this, containerId, opts)
    }
  }
  // static 静态方法调用则直接使用CsmMap.方法
  static Popup = Popup
  /**
  * 自定义事事件回调
  * @param {String} type 事件类型
  * @param {Function} callback 回调函数
  */
  on (type, callback) {
    if (typeof callback !== 'function') return
    if (!this._eventList) {
      this._eventList = {}
    }
    this._eventList[type] = callback
    this.listen(type, callback)
  }
  /**
   * 设置视图聚焦点
   * @param {Array} opts.coordinate 三维坐标
   * @param {Number} opts.heading 航向角
   * @param {Number} opts.pitch 倾斜角
   * @param {Number} opts.roll 滚动角
   */
  setView (opts) {
    const {
      coordinate = [],
      heading = 0,
      pitch = -90,
      roll = 0
    } = opts || {}
    const [longitude, latitude, height = 0] = coordinate
    this.viewer.camera.setView({
      destination: Cesium.Cartesian3.fromDegrees(longitude, latitude, height),
      orientation: {
        heading: Cesium.Math.toRadians(heading),
        pitch: Cesium.Math.toRadians(pitch),
        roll: Cesium.Math.toRadians(roll)
      }
    })
  }
  /**
   * 平滑移动到指定位置
   * @param {Array} opts.coordinate 三维坐标
   * @param {Number} opts.heading 航向角
   * @param {Number} opts.pitch 倾斜角
   * @param {Number} opts.roll 滚动角
   * @param {Number} opts.duration 动画持续时间（秒）
   * @param {Fuction} opts.complete 飞行完事件
   * @param {Fuction} opts.cancel 取消飞行完事件
   */
  flyTo (opts) {
    const {
      coordinate = [],
      duration,
      heading = 0,
      pitch = -90,
      roll = 0,
      complete,
      cancel
    } = opts || {}
    const [longitude, latitude, height = 0] = coordinate
    const config = {
      destination: Cesium.Cartesian3.fromDegrees(longitude, latitude, height),
      duration, // 飞行所用时间
      orientation: {
        heading: Cesium.Math.toRadians(heading),
        pitch: Cesium.Math.toRadians(pitch),
        roll: Cesium.Math.toRadians(roll)
      }
    }
    complete && isFunction(complete) && Object.assign(config, { complete })
    cancel && isFunction(cancel) && Object.assign(config, { cancel })
    this.viewer.camera.flyTo(config)
  }
}

const prototypeMethods = {
  ...entityMethods,
  ...layerMethods,
  ...primitiveMethods,
  ...coordinateUtils
}
Object.keys(prototypeMethods).forEach((key) => {
  Object.defineProperty(
    CsmMap.prototype,
    key,
    { value: prototypeMethods[key] }
  )
})

/**
 * 初始化地图
 * @param {*} containerId 容器id
 * @param {*} options 配置项
 */

function initMap (containerId, options) {
  const {
    accessToken,
    geocoder,
    homeButton,
    sceneModePicker,
    baseLayerPicker,
    navigationHelpButton,
    animation,
    timeline,
    fullscreenButton,
    // terrainProvider,
    imageryProvider,
    // 高程url地址
    terrainUrl,
    // 中心位置
    center = []
  } = options || {}

  Cesium.Ion.defaultAccessToken = accessToken || TOKEN

  const terrainProvider = terrainUrl ? new Cesium.CesiumTerrainProvider({
    url: terrainUrl,
    requestVertexNormals: true, // 是否请求顶点法线，根据需求设置
    requestWaterMask: true // 是否请求水域遮罩，根据需求设置
  }) : Cesium.createWorldTerrain()

  const viewer = new Cesium.Viewer(containerId, {
    geocoder,
    homeButton,
    sceneModePicker,
    baseLayerPicker,
    navigationHelpButton,
    animation,
    timeline,
    fullscreenButton,
    imageryProvider,
    terrainProvider,
  })
  // 赋值给对象
  Object.defineProperty(this, 'viewer', { value: viewer })
  // 隐藏版权信息
  viewer._cesiumWidget._creditContainer.style.display = 'none'
  // 抗锯齿
  viewer.scene.fxaa = true
  // 加载底图
  loadImageryLayers.call(this, options)

  // 设置默认视图位置
  this.setView({ coordinate: center })

  // 地图加载完成
  this.trigger(LOAD_EVENT_NAME)
}
/**
 * 缓存图层
 */
function initCache () {
  const cache = {
    // 图层
    imageryLayer: {}
  }

  Object.defineProperty(this, '_setImageryLayer', {
    value: function (key, data) {
      if (data === null) {
        delete cache.imageryLayer[key]
      }
      cache.imageryLayer[key] = data
    }
  })

  Object.defineProperty(this, '_getImageryLayer', {
    value: function (key) {
      return key ? cache.imageryLayer[key] : cache.imageryLayer
    }
  })
}
/**
 * 加载资源
 * @param {*} config 
 */
async function loadRecource (config) {
  const { jsUrl, cssUrl } = config || {}
  if (!window.Cesium) {
    // 加载css
    cssUrl && loadCss(cssUrl)
    // 加载js
    return await loadJs(jsUrl)
  }
}
/**
 * 加载底图
 * @param {*} data 
 */
function loadImageryLayers (data) {
  const { WMTSImageries = [], UTImageries = [], showLayerIds = [] } = data || {}
  // 移除默认底图
  this.viewer.imageryLayers.remove(this.viewer.imageryLayers.get(0))
  // 加载WMTS
  if (WMTSImageries.length) {
    loadWMTSImageries.call(this, WMTSImageries)
  }
  if (UTImageries.length) {
    loadUTImageries.call(this, UTImageries)
  }
  // 设置显示的底图
  if (showLayerIds.length) {
    layerMethods.setImageryLayerShow.call(this, showLayerIds)
  }
}
/**
 * 加载WMTS
 * @param {*} Imageries 
 */
function loadWMTSImageries (WMTSImageries = []) {
  WMTSImageries.forEach((v) => {
    const {
      id,
      url,
      layer, // WMTS请求的层名称
      name,
      style = 'default', //  WMTS请求的样式名称
      format,
      tileMatrixSetID, // //用于WMTS请求的TileMatrixSet的标识符
      subdomains, //服务负载子域  URL模板中用于 {s} 占位符的子域 服务负载子域 
      maximumLevel = 18
    } = v
    const provider = new Cesium.WebMapTileServiceImageryProvider({
      url,
      layer,
      style,
      format,
      tileMatrixSetID,
      subdomains,
      maximumLevel,
      name,
      credit: new Cesium.Credit(name),
    })
    const imageryLayer = this.viewer.imageryLayers.addImageryProvider(provider)
    // 缓存imageryLayer
    this._setImageryLayer(id, imageryLayer)
  })
}
/**
 * 通过UrlTemplate添加imageryLayer
 * @param {*} Imageries 
 */
function loadUTImageries (UTImageries = []) {
  UTImageries.forEach((v) => {
    const { id, url } = v
    const provider = new Cesium.UrlTemplateImageryProvider({ url })
    const imageryLayer = this.viewer.imageryLayers.addImageryProvider(provider)
    // 缓存imageryLayer
    this._setImageryLayer(id, imageryLayer)
  })
}